package control;

import java.util.Vector;

import storage.ManageData;

import basic.CRUDable;
import basic.Manager;

import domain.Category;

//@author A0098891H
public class CategoryManager extends Manager{
	
	private Vector<Category> _listOfCategories;
	
	/**
	 * @param o
	 */
	public CategoryManager(Object o)
	{
		this.initialize();
	}
	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public Vector<Category> getList()
	{
		return this._listOfCategories;
	}
	//auto-generated id
	/**
	 *
	 * @see basic.Manager#add(basic.CRUDable)
	 * @param category
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public int add(CRUDable category) {
		//check uniqueness
		for(Category c:_listOfCategories)
		{
			if(c.isSame((Category)category))
				return -1;
		}
		category.set_id(_nextId++);
		_listOfCategories.add((Category)category);
		return category.get_id();
	}
	//add with id

	/**
	 *
	 * @see basic.Manager#addTo(basic.CRUDable, int, int)
	 * @param category
	 * @param id
	 * @param pos
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean addTo(CRUDable category, int id, int pos) {
		//check uniqueness
		for(Category c:_listOfCategories)
		{
			if(c.isSame((Category)category))
				return false;
		}
		category.set_id(id);
		_listOfCategories.add(pos,(Category)category);
		return true;
	}
	
	/**
	 * 
	 * @param category
	 * @param id
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public boolean addSubCategory(Category category, int id)
	{
		for(Category c: _listOfCategories)
		{
			if(c.get_id()==id)
			{
				category.set_id(_nextId++);
				if(!category.hasSubCategory())
				{
					category.initSubCategory();
				}
				if(c.addSubCategory(category))
					return true;
			}
		}
		return false;
	}

	/**
	 *
	 * @see basic.Manager#get(int)
	 * @param id
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public Category get(int id) {
		for(Category c: _listOfCategories)
		{
			if(c.get_id()==id)
			{
				return c;
			}
		}
		return _listOfCategories.get(0);
	}

	/**
	 *
	 * @see basic.Manager#edit(int, basic.CRUDable)
	 * @param id
	 * @param category
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public Category edit(int id, CRUDable category) {
		if(id==1)
			return null;
		for(Category c:_listOfCategories)
		{
			if(c.isSame((Category)category)&&c.get_id()!=id)
				return null;
		}
		for(Category c:_listOfCategories)
		{
			if(c.get_id()==id)
			{
				Category cate = new Category(c);
				c.set((Category)category);
				return cate;
			}
		}
		return null;
	}

	//delete by id
	/**
	 *
	 * @see basic.Manager#delete(int)
	 * @param id
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public Category delete(int id) {
		if(id<2)
			return null;
		for(Category c:_listOfCategories)
		{
			if(c.get_id()==id)
			{
				if(this._listOfCategories.remove(c))
					return c;
				else 
					return null;
			}
		}
		return null;
	}
	
	public int deleteFrom(int id) {
		if(id<2)
			return -1;
		int i = 0;
		for(Category c:_listOfCategories)
		{
			if(c.get_id()==id)
			{
				if(this._listOfCategories.remove(c))
					return i;
				else 
					return -1;
			}
			i++;
		}
		return -1;
	}
	
	/**
	 *
	 * @param cmd
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public int findID(String categoryName) {
		for (Category c: _listOfCategories) {
			if (c.get_name() == categoryName)
				return c.get_id();
		}
		
		return -1;
	}
	/**
	 *
	 * @see interfaces.CommandLine#readCmd(java.lang.String)
	 * @param cmd
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public int readCmd(String cmd) {
		//<CATEGORY>|<ADD> <NAME> (COMPULSORY)
		//<CATEGORY>|<EDIT> <ID> <NAME> (COMPULSORY) 
		//OPTIONAL BUT NEED PARAMETER s:<SUBCATEGORY_OF(ID)>
		
		//<CATEGORY>|<DELETE> <ID>
		//<CATEGORY>|<VIEW> <ID>
		//<CATEGORY>|<UNDO>
	
		//check undo
		boolean isUndo = false;
		boolean isRedo = false;
		if(cmd.trim().compareToIgnoreCase("undo")==0)
		{
			if(!_undoStack.isEmpty())
			{
				isUndo = true;
				cmd = _undoStack.pop();
				int iAction = cmd.indexOf('[');
				int isavedId = cmd.indexOf('&');
				int isavedPos = cmd.indexOf(']');
				_undoAction = Integer.parseInt(cmd.substring(0, iAction));
				_savedId = Integer.parseInt(cmd.substring(iAction+1, isavedId));
				_savedPos = Integer.parseInt(cmd.substring(isavedId+1, isavedPos));
				
				if(_undoAction==ACTION_DELETE || _undoAction==ACTION_EDIT)
				{
					_redoStack.add(cmd.substring(0,cmd.indexOf('|')));
					cmd = cmd.substring(cmd.indexOf('|')+1);
				}
			}
		}
		else if(cmd.trim().compareToIgnoreCase("redo")==0)
		{
			if(!_redoStack.isEmpty())
			{
				isRedo = true;
				cmd = _redoStack.pop();
				if(cmd.indexOf(']')!=-1)
				{
					int iAction = cmd.indexOf('[');
					int isavedId = cmd.indexOf('&');
					int isavedPos = cmd.indexOf(']');
					_undoAction = Integer.parseInt(cmd.substring(0, iAction));
					_savedId = Integer.parseInt(cmd.substring(iAction+1, isavedId));
					_savedPos = Integer.parseInt(cmd.substring(isavedId+1, isavedPos));
					cmd = cmd.substring(cmd.indexOf(']')+1);
				}
			}	
		}
		
		String str[] = cmd.split(",");
		for(int i=0;i<str.length;i++)
		{
			str[i] = str[i].trim();
		}
		int length = str.length;
		
		if(length < 2)
			return -1;

		switch(str[1].toLowerCase())
		{
		case "add":
			if(length < 3) 
				return -1;
			Category category = new Category();
			category.set_name(str[2]);
			_returnedObject = category;
			if(isUndo && _undoAction==ACTION_ADD)
			{
				int _savedPos = deleteFrom(_savedId);
				if(_savedPos >= 0)
				{//must record the position and id for redo
					_redoStack.add(-1+"["+_savedId+"&"+_savedPos+"]"+cmd);
					return _undoAction;
				}
				else
					return -1;//undo failed
			}
			if(isUndo && _undoAction==ACTION_DELETE)
			{
				if(!addTo(category,_savedId,_savedPos))
					return -1;
				return _undoAction;
			}
			if(isRedo)
			{
				if(!addTo(category,_savedId,_savedPos))
					return -1;
			}
			else
			{
				_savedId = add(category);
			}
			if(_savedId<0)
				return -1;
			_action = ACTION_ADD;
			
			break;
		case "delete":
			if(length < 3)
				return -1;
			_savedId = Integer.parseInt(str[2]);
			Category deletedCategory = get(_savedId);
			if(deletedCategory==null)
				return -1;
			_savedPos = this._listOfCategories.indexOf(deletedCategory);
			deletedCategory = delete(_savedId);
			if(deletedCategory==null)
				return -1;
			_returnedObject = deletedCategory;
			cmd+="|category,add,"+deletedCategory.writeToString();
			_action = ACTION_DELETE;
			break;
			
		case "edit":
			if(length < 4)
				return -1;
			_savedId = Integer.parseInt(str[2]);
			Category updatedCategory = new Category();
			updatedCategory.set_name(str[3]);
			Category obsoleteCategory = edit(_savedId,updatedCategory);
			if(obsoleteCategory==null)
				return -1;
			_returnedObject = obsoleteCategory;
			if(isUndo && _undoAction==ACTION_EDIT)
			{
				return _undoAction;
			}
			cmd+="|category,edit,"+_savedId+","+obsoleteCategory.writeToString();
			_action = ACTION_EDIT;
			break;
		default:
			return -1;
		}

		_undoStack.add(_action+"["+ _savedId +"&"+ _savedPos +"]"+cmd);
		
		return _action;
	}
	
	/**
	 * 
	 * @param catManager
	 * @Precondition 
	 * @Postcondition 
	 */
	public void set(CategoryManager catManager)
	{
		super.set(catManager);
		this._listOfCategories = catManager._listOfCategories;
	}

	/**
	 *
	 * @see basic.Manager#initialize()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean initialize() {
		if(loadAll()==false)
		{
			super.initialize();
			this._listOfCategories = new Vector<Category>();
			Category cate = new Category();
			cate.set_name("UNCATEGORIZED");
			Category cate1 = new Category();
			cate1.set_name("LOAN PAID");
			add(cate);
			add(cate1);

			String [] initCate = {"AUTOMOBILE","BILL","ENTERTAINMENT","FOOD","GROCERIES",
					"GIFTS", "HEALTH", "HOUSING","TUITION FEES","TRANSPORTATION","UTILITIES"};
			for(String name: initCate)
			{
				Category c = new Category();
				c.set_name(name);
				add(c);
			}
		}
		return true;
	}


	/**
	 *
	 * @see basic.Manager#saveAll()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean saveAll() {
		return ManageData.saveCategoryManagerToFile(this);
	}

	/**
	 *
	 * @see basic.Manager#loadAll()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean loadAll() {
		CategoryManager catManager = ManageData.loadCategoryManagerFromFile();
		if(catManager==null)
			return false;
		
		this.set(catManager);
		
		return true;
	}

	/**
	 *
	 * @see java.lang.Object#toString()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		for(Category category : this._listOfCategories)
		{
			sb.append("  ("+category.get_id()+") "+category.toString() + "\n");
		}
		return sb.toString();
	}
	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public int size()
	{
		return _listOfCategories.size();
	}
}
